﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Chromos.Actors;
using Chromos.AIEngine;

namespace Chromos.PhysicsEngine
{
	public enum PhysicsType
	{
		FULL,
		NONE
	}

	public class Physics
	{
		protected Game1 game;
		protected int timeDelta = 0;
		protected float gravityForce = 9.8f*74;
		protected float frictionForce = .07f;

		public Physics(Game1 game)
		{
			this.game = game;
		}
		
		RectangleF r = new RectangleF(0, 0, 0, 0);
		float totalWidthCollision = 0;
		float totalHeightCollision = 0;
		float maxWidthCollision = 0;
		float maxHeightCollision = 0;
		Sprite.Axis maxWidthSprite;
		Sprite.Axis maxHeightSprite;
		public void update()
		{
			timeDelta = game.timeSinceLastUpdate;

			foreach (Projectile p in game.projectiles)
			{
				updateActor(p);
			}

			// Loop through enemies
			foreach (Actor a in game.enemies)
			{
				updateActor(a);
			}

			updateActor(game.player);
		}

		/// <summary>
		/// Updates the velocity, position, and collision of an actor
		/// </summary>
		/// <param name="actor"></param>
		protected void updateActor(Actor actor)
		{
			totalWidthCollision = totalHeightCollision = maxWidthCollision = maxHeightCollision = 0;

			if(actor.applyGravity)
				actor.velocity.Y += gravityForce * ((float)timeDelta / 1000);

			actor.position += actor.velocity * ((float)timeDelta / 1000);

			if (sr == null)
				sr = new RectangleF(0, 0, 0, 0);

			// Check for an enemy colliding with player
			//Sprite.Axis playerAxis = new Sprite.Axis((int)game.player.position.X, (int)game.player.position.Y, (int)game.player.size.X, (int)game.player.size.Y, 1, null);
			/*if (actor == game.player)
			{
				foreach (Actor act in game.enemies)
				{
					if (!act.getDead() && actor.boundingBox.Intersects(act.boundingBox))
					{
						actor.position -= actor.velocity * ((float)timeDelta / 1000);
					}
				}
			}*/
			/*if (actor is Enemy && !actor.getDead() && game.player.boundingBox.Intersects(actor.boundingBox))
			{
				//((PlayerAIEngine)game.player.getAIEngine()).kill();
				actor.position -= actor.velocity * ((float)timeDelta / 1000);
			}*/
			// Check for projectile collision
			if (!actor.getDead())
			{
				foreach (Projectile p in game.projectiles)
				{
					if (p.boundingBox.Intersects(actor.boundingBox))
					{
						actor.collidedProjectile(p);
					}
				}
			}


			// Loop through the walls and check collision
			foreach (Sprite.Axis s in game.mapSprites)
			{
				sr.X = s.returnX();
				sr.Y = s.returnY();
				sr.Width = s.returnWidth();
				sr.Height = s.returnHeight();

				if (sr.Intersects(actor.boundingBox))
				{
					if (actor is Projectile)
					{
						//Console.Out.WriteLine(s.returnX() + " " + actor.position.X + " " + s.ToString() + " ");
						(actor as Projectile).wallCollide();
					}

					RectangleF.Intersect(sr, actor.boundingBox, r);
					totalWidthCollision += r.Width;
					totalHeightCollision += r.Height;
					if (r.Width > maxWidthCollision)
					{
						maxWidthCollision = r.Width;
						maxWidthSprite = s;
					}
					if (r.Height > maxHeightCollision)
					{
						maxHeightCollision = r.Height;
						maxHeightSprite = s;
					}
                }
			}

			// Player is lined on top or bottom with a wall
			if (totalWidthCollision >= actor.boundingBox.Width - 12)
			///if (totalWidthCollision >= actor.boundingBox.Width)
			{
				// Sprite below player
				if (maxWidthSprite.Y >= actor.boundingBox.Y)
				{
					actor.setBounding(actor.boundingBox.X, maxWidthSprite.Y - maxWidthSprite.Height - 0.1f);
                    // Since player is on floor, he is not jumping.
                    if (actor.getAIEngine().getJumping())
                        actor.getAIEngine().setNotJumping();
                }
				else
				{
					actor.setBounding(actor.boundingBox.X, maxWidthSprite.Y + maxWidthSprite.Height + 0.1f);
				}
				if (actor.getDead())
					actor.velocity -= frictionForce * actor.velocity;
				actor.hasGrounded = true;
				actor.velocity.Y = 0;
			}
			// Player is lined on left or right with a wall
			if (totalHeightCollision >= actor.boundingBox.Height - 12)
			///if (totalHeightCollision >= actor.boundingBox.Height)
			{
				if (actor is Enemy)
				{
					//if (actor.getDead())
					//	actor.velocity -= frictionForce * actor.velocity;
					actor.getAIEngine().collided();
				}
                // Sprite left of player
				if (maxHeightSprite.X <= actor.boundingBox.X)
				{
					actor.setBounding(maxHeightSprite.X + maxHeightSprite.Width + 1.5f, actor.boundingBox.Y);
				}
                // Sprite right of player
				else
				{
					actor.setBounding(maxHeightSprite.X - actor.boundingBox.Width - 1.5f, actor.boundingBox.Y);
				}
			}
			// Some corner. If either top corner, prevent X movement. Else Y
			if (totalHeightCollision < (actor.boundingBox.Height - 12) && totalWidthCollision > 0 && totalWidthCollision < (actor.boundingBox.Width - 12))
			///if (totalHeightCollision < (actor.boundingBox.Height) && totalWidthCollision > 0 && totalWidthCollision < (actor.boundingBox.Width))
			{
				// Sprite below player
				if (maxWidthSprite.Y >= actor.boundingBox.Y && maxHeightCollision < actor.boundingBox.Height / 4)
				{
					actor.setBounding(actor.boundingBox.X, maxWidthSprite.Y - maxWidthSprite.Height - 0.1f);
					if (actor.getDead())
						actor.velocity -= frictionForce * actor.velocity;
					actor.hasGrounded = true;
					actor.velocity.Y = 0;
				}
				///if(maxHeightCollision > actor.boundingBox.Height / 4)
				else
				{
					if (actor is Enemy)
                        actor.getAIEngine().collided();
					// Sprite left of player
					if (maxHeightSprite.X <= actor.boundingBox.X)
					///if (maxHeightSprite.X <= actor.boundingBox.X + 6)
					{
						actor.setBounding(maxHeightSprite.X + maxHeightSprite.Width + 1.5f, actor.boundingBox.Y);
					}
					// Sprite right of player
					else
					{
						actor.setBounding(maxHeightSprite.X - actor.boundingBox.Width - 1.5f, actor.boundingBox.Y);
					}
				}
			}

            if (actor is Player && totalHeightCollision == 0 && totalWidthCollision == 0 && !actor.getAIEngine().getJumping())
                actor.getAIEngine().setJumping();
		}

		
		/// <summary>
		/// Returns the projection of the vector a onto b
		/// </summary>
		/// <param name="s"></param>
		/// <param name="a"></param>
		/// <returns></returns>
		Vector2 sav = Vector2.Zero;
		protected Vector2 vProjection(Vector2 a, Vector2 b)
		{
			float d1 = Vector2.Dot(a, b);
			float d2 = Vector2.Dot(b, b);
			float d3 = d1 / d2;
			return new Vector2(d3 * b.X, d3 * b.Y);
		}


		RectangleF sr = new RectangleF(0, 0, 0, 0);
		//Rectangle ar = Rectangle.Empty;
		protected bool squareCollide(Sprite.Axis s, Actor a)
		{
			sr.X = s.returnX();
			sr.Y = s.returnY();
			sr.Width = s.returnWidth();
			sr.Height = s.returnHeight();

			// This makes the bounding rectangle smaller
			//ar.Inflate(-a.boundingBox.X, -a);
			return sr.Intersects(a.boundingBox);
		}
	}
}
